Skip to content

feat(integrations): add Streamlit UI wrapper for the humanizer skill#102

Closed
Rich627 wants to merge 6 commits into
blader:mainfrom
Rich627:feat/streamlit-ui
Closed

feat(integrations): add Streamlit UI wrapper for the humanizer skill#102
Rich627 wants to merge 6 commits into
blader:mainfrom
Rich627:feat/streamlit-ui

Conversation

@Rich627
Copy link
Copy Markdown

@Rich627 Rich627 commented Apr 24, 2026

Summary

Adds a minimal Streamlit UI at integrations/streamlit/ that wraps the humanizer skill. Paste text → click Humanize → get the cleaned rewrite back with a native copy button, without the skill's default draft / audit / summary sections.

This is the first non-skill code in the repo. Everything is scoped under integrations/streamlit/ so the skill itself is untouched. If you'd prefer to keep this repo pure-skill, I'm happy to move the wrapper out to a separate repo and link from the root README instead — just say the word.

What the wrapper does

  • Shells out to the host's claude CLI (claude -p --no-session-persistence …), so it reuses the user's existing Claude Code OAuth login — no API key required. ANTHROPIC_API_KEY is honored as a fallback.
  • Wraps the user's text in a prompt that invokes the humanizer skill but adds strict output constraints so the returned text is just the rewritten prose — no "Draft rewrite" / "What makes this AI" / "Summary of changes" sections.
  • Defensive post-process regex (_PREAMBLE_RE + _TRAILING_RE) strips any leftover preamble or skill-trailer block if the model ever disobeys. The trailing regex is anchored to the skill's actual bolded headers (e.g. **Summary of changes:**, **Changes made:**) so legitimate prose that merely starts with "Notes" or "Summary" is preserved.
  • Renders the result via st.code(wrap_lines=True) so there's a native copy-to-clipboard icon.

Ways to run

  • Local: cd integrations/streamlit && ./run.sh — creates a venv, installs streamlit, opens http://localhost:8501.
  • Docker: cd integrations/streamlit && docker compose up --build. The compose file mounts ~/.claude/ into the container (RW, since macOS may need claude /login inside the container), runs Streamlit as a non-root user (uid 1000), and binds port 127.0.0.1:8501 so the unauthenticated UI isn't exposed to the LAN by default.

Files added

```
integrations/streamlit/
├── app.py # Streamlit UI + subprocess call to claude -p
├── run.sh # Local launcher (venv + streamlit)
├── Dockerfile # python + node + claude CLI + streamlit (non-root user)
├── docker-compose.yml # Mounts ~/.claude, loopback-bound :8501
├── .dockerignore
├── requirements.txt # streamlit>=1.56
└── README.md # Lifecycle / auth / stop / output / security docs
```

Notes for review

  • Each click spawns a fresh claude -p --no-session-persistence subprocess — no state survives across clicks, nothing is written to the session store.
  • Clicking Humanize again kills any still-running subprocess first.
  • On Streamlit shutdown (Ctrl+C / docker compose down), an atexit hook (registered exactly once via st.cache_resource, not per click) kills any in-flight subprocess.
  • README has a Security considerations section covering the ~/.claude trust boundary, the non-root container user, the loopback port bind, and the single-user scope.

Test plan

  • Local: cd integrations/streamlit && ./run.sh, paste AI-sounding text, click Humanize, confirm cleaned output appears with a copy icon.
  • Local: confirm Ctrl+C in the terminal cleanly stops Streamlit and kills any in-flight claude subprocess.
  • Docker (Linux): docker compose up --build, same flow. (not tested locally — no Linux host available; Dockerfile is identical to the macOS path so this is expected to work, but flagging for reviewer verification.)
  • Docker (macOS): after docker compose up -d --build, run docker compose exec humanizer claude /login once, then humanize.
  • API-key path: ANTHROPIC_API_KEY=sk-ant-… ./run.sh, confirm no OAuth prompt. (not tested — no spare API key on hand; the code path is a single os.environ pass-through to the claude subprocess, which is the documented override.)
  • Regression guard: paste prose beginning with words like "Notes" / "Summary" / "Changes" and confirm it is preserved intact by the cleanup regex.

🤖 Generated with Claude Code

Rich627 and others added 6 commits April 23, 2026 17:21
Paste text → click Humanize → get the final rewritten prose back.
The wrapper shells out to the local `claude` CLI with a prompt that invokes
the humanizer skill but suppresses its usual draft / audit / summary output,
leaving only the rewritten text for copy-paste workflows.

Two ways to run:
- Local: ./run.sh (venv + streamlit)
- Docker: docker compose up --build (mounts ~/.claude for skill + auth)

No API key required — uses the host's existing Claude Code login.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
README now explains:
- exactly when the `claude` subprocess starts/stops (per-click one-shot)
- OAuth (`claude /login`) vs ANTHROPIC_API_KEY, including the macOS Docker
  keychain caveat and how to work around it
- what guarantees the output stays free of draft/audit/summary noise

app.py adds a `_clean_output()` pass that strips leaked preambles and
trailing "Summary of changes" blocks, and replaces the streamed view with
a copy-friendly textarea once generation finishes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Output now renders via st.code (wrap_lines=True) so it has a native
  copy-to-clipboard icon in the top-right corner.
- Add a visible "Running humanizer skill…" status message + spinner so
  users know work is in progress instead of only seeing the tiny Stop
  button in the Streamlit header.
- Streaming response is preserved — text appears live as it arrives,
  then gets replaced by the copy-ready code block on completion.
- README's Lifecycle section now spells out exactly how to stop:
  Ctrl+C for local, docker compose down / stop / kill for container,
  and notes that closing the browser tab alone doesn't stop Streamlit
  or the subprocess (which will self-terminate within seconds anyway).
- Bump streamlit minimum to 1.56 (wrap_lines support + general latest).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bundles the code-review + security-review fixes with a couple of
usability nits reported during testing.

Correctness:
- Tighten `_TRAILING_RE` so it only strips the humanizer skill's actual
  trailer blocks (bolded "Summary of changes" / "Changes made" /
  "What makes … AI …" / "Draft rewrite" / "Now make it not …", with or
  without a leading `---` separator). Previous pattern ate any paragraph
  whose first word was "Notes" / "Summary" / "Changes" — a real problem
  for a humanizer whose job is preserving user prose.
- Add `--no-session-persistence` to the `claude -p` call so no session
  file is written to disk, guaranteeing each click is fully stateless
  with zero possibility of one run's context leaking into the next.

Resource hygiene:
- Register atexit exactly once via `@st.cache_resource` instead of per
  click. Previous code appended a new atexit hook to every Humanize
  press for the life of the Streamlit process.

Docker hardening:
- Dockerfile now creates an unprivileged `app` user (uid 1000), chowns
  /app, and runs Streamlit + claude CLI as that user. Previously ran
  as root, so a dependency bug could write host files as uid 0 via
  the ~/.claude mount.
- docker-compose.yml binds the published port to 127.0.0.1 only so the
  unauthenticated UI isn't exposed to the LAN by default, and adjusts
  the mount target + HOME to the new non-root home.

UX:
- Status message no longer wraps "claude -p" in backticks (rendered as
  tiny inline code and broke mid-sentence in the info block). Plain
  English now.
- README: new Security considerations section covering the port bind,
  the ~/.claude mount trust boundary, the non-root user, and the
  single-user scope. Lifecycle section mentions --no-session-persistence
  explicitly so users don't worry about state leaking between clicks.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The `claude -p` text mode doesn't actually deliver token-by-token
streaming through the PIPE in a way Streamlit's write_stream can
render incrementally, so the "streaming" UX was a lie. Swap in a
plain `proc.communicate()` inside st.spinner, render once on
completion via st.code (which still has the native copy button).

Also update README — lifecycle table + Output section no longer claim
text "streams in live".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The app supports both OAuth login and ANTHROPIC_API_KEY paths, so
phrasing the caption as "No API key required" understates the API-key
path and can mislead users who already have a key set.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@blader
Copy link
Copy Markdown
Owner

blader commented May 27, 2026

Appreciate the effort on this. Closing because a Streamlit + Docker web UI is a maintenance commitment separate from the skill prompt itself, and out of scope for this repo. It'd make a great standalone project that depends on the skill.

@blader blader closed this May 27, 2026
duathron added a commit to duathron/humanizer-ext that referenced this pull request May 27, 2026
5 OQs from the seed-catalogue extraction answered + appended to
docs/de-seed-catalogue.md as a binding decisions log for Task 5 + Task 6:

- OQ1: build DE blader#7 via Opus + Wikipedia-AI-Cleanup-Editor manual curation
- OQ2: include all 3 DE-only patterns (blader#102 Konjunktiv II, blader#103 Anglizismen,
  blader#104 Nominalstil)
- OQ3: exclude all 6 Wikipedia-context-only entries from patterns/de.md
- OQ4: include EN-PARALLEL blader#8 with DE forms (gilt als / dient als / etc.);
  verify in Task 5 mining
- OQ5: defer universal pattern DE-token extensions to Task 6 follow-up

Numbering reshuffle: blader#100 + blader#101 reserved for prose-applicable DE-only
patterns surfaced during Task 5; blader#102 + blader#103 + blader#104 firm assignments per OQ2.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
duathron added a commit to duathron/humanizer-ext that referenced this pull request May 28, 2026
…ources (Phase 2 Task 4)

All $0 sources per maintainer /goal 'minimize Phase 2 Task 4 budget'.

Source A (Wikipedia DE AI-Cleanup tagged articles, 30 docs, CC-BY-SA-3.0):
  Real-world DE prose flagged by humans as AI-suspected via the
  Vorlage:KI-generiert template. Fetched via embeddedin API (50+ tagged
  articles available; sampled 30 with fixed seed 42). Mix of substantial
  AI tells and borderline cases — human-verified suspect baseline.
  Examples: Sara Noxx, Digitales Schlafmonitoring, Synthetische Daten,
  Verband evangelischer Pfarrerinnen und Pfarrer, Moonton, Hybridtechnik.

Source B (Claude CLI subscription generation, 90 docs, MIT):
  6 domains × 5 topics × 3 models (sonnet/haiku/opus) = 90 samples via
  `claude -p` subscription ($0). DE prompt templates ask for stereotypical
  AI-style content. Cross-model variation for intra-Anthropic idiolect
  diversity. ANTHROPIC_API_KEY stripped from subprocess env per
  _shared.run_skill convention.

Source C (Opus main-thread inline synthesis, 12 docs, MIT):
  2 samples per domain × 6 domains. Engineered to exercise specific DE tells:
  blader#7 AI vocabulary, blader#102 Konjunktiv II stacking, blader#103 Anglizismen-Leakage,
  blader#104 Nominalstil-Inflation, plus EN-parallels blader#22/blader#10/blader#15/blader#16/blader#23/blader#24/
  blader#32/blader#36/blader#37. Act as calibration anchors for per-pattern eval testing.

Total: 132 docs / 728 KB. Comfortably exceeds plan target of 75-100.
Sufficient signal volume for Task 5 mine_patterns.py LLR scoring against
the 46-doc human corpus (340 KB). AI/human ratio 2.1× by docs, 2.1× by KB.

All redistributable license (CC-BY-SA-3.0 for Wikipedia, MIT for Anthropic-
generated + Opus inline). No fair-use research-only content needed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
duathron added a commit to duathron/humanizer-ext that referenced this pull request May 28, 2026
…up consensus

Mined the DE corpus (132 AI / 46 human docs) via mine_patterns.py LLR
scoring. Top 100 candidates routed through 3-voice writer persona panel
(Academic + Marketing Copywriter + Journalist) for ✓/✗/◐ vote per ngram.

Consolidated keep-list at docs/de-mined-patterns.md (saved this commit):

Strong consensus (unanimous ✓) — into patterns/de.md Task 6:
  blader#7 DE AI Vocabulary additions:
    darüber hinaus, zusammenfassend, ganzheitliche, vorliegenden,
    der vorliegenden, umfassende (cluster only), darstellt
  blader#100 (NEW reserved DE-only):
    Anchor: 'im Rahmen der vorliegenden [Arbeit/Studie/Untersuchung]'
    DE academic-frame boilerplate — no EN equivalent, highest LLR among
    DE-only candidates (rank blader#32, LLR 31.97, 31:0 ratio)
  blader#101 (NEW reserved DE-only):
    Anchor: '[es/zusammenfassend] lässt sich [sagen/feststellen/festhalten]'
    DE impersonal-reflexive AI hedge — no EN equivalent, multiple high-LLR
    forms (lässt sich rank blader#5 LLR 93.73; zusammenfassend lässt sich sagen
    rank blader#29 LLR 33.00)
  blader#12 DE meta-commentary extensions:
    zusammenfassend, wichtig zu (beachten/betonen), full blader#101 family

Cluster-only (◐ ADJUST) — flag with threshold logic:
  zentrale Rolle, umfassende, implementierung (non-tech), überzeugt (unanchored)

Skip (artifacts + common DE):
  hedging (metadata), queens (Wikipedia bleed), substantivketten / übergänge
  (Source C body refs), dass / es / sich / ich / meine / bin / mich (common
  DE function words / first-person genre artifact)

Mining-script bug noted for v3.6.0: YAML frontmatter strip catches headers
but Opus inline synthesis demonstrably references metadata terms in body
(tells_targeted leaks via prose). Workaround: drop tells_targeted from
synthesis frontmatter next pass.

OQ assignments updated:
  blader#100 reassigned from 'first prose-applicable DE-only Wiki pattern surfaced
       during mining' (per maintainer doc) to the academic-frame boilerplate
       discovered as highest-LLR DE-only signal
  blader#101 reassigned to impersonal-reflexive Nominalstil sub-pattern
  blader#102/blader#103/blader#104 remain Konjunktiv II / Anglizismen / Nominalstil per
       maintainer OQ2 decision (still open for Task 6 implementation)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
duathron added a commit to duathron/humanizer-ext that referenced this pull request May 28, 2026
878-line German pattern pack mirroring patterns/en.md structure + extending
with 5 DE-only patterns (blader#100-blader#104) per maintainer decisions + Task 5 mining
consensus.

EN-PARALLEL patterns (translated to DE with DE-specific trigger words +
before/after examples): blader#1, blader#2, blader#3, blader#4, blader#5, blader#7, blader#8, blader#9, blader#10, blader#11, blader#12, blader#13,
blader#16, blader#20, blader#21, blader#22, blader#23, blader#24, blader#27, blader#28, blader#30, blader#31, blader#32, blader#33, blader#34, blader#35, blader#36,
blader#37 (28 patterns).

DE-only patterns (blader#100-blader#104, no EN equivalent):

  blader#100 Akademische Rahmen-Floskel — 'im Rahmen der vorliegenden
       [Arbeit/Studie/Untersuchung/Analyse]' bureaucratic self-reference.
       Mining-derived (LLR 31.97, 31:0 AI:human).

  blader#101 Impersonales Reflexiv — '[es/zusammenfassend] lässt sich
       [sagen/feststellen/festhalten/zeigen]' AI hedge construction.
       Mining-derived (LLR 93.73 bigram + 33.00 four-gram).

  blader#102 Konjunktiv II Stacking — 3+ würde/wäre/hätte/könnte forms in close
       proximity for vague hedging. Per maintainer OQ2.

  blader#103 Anglizismen-Leakage — denglisch business buzzwords (insight,
       deliver, leveragen, Pain Points, ganzheitliche Customer Journey).
       Per maintainer OQ2.

  blader#104 Nominalstil-Inflation — noun-heavy bureaucratic verbing
       ('die Durchführung der Analyse' vs 'analysieren'). Per maintainer
       OQ2.

DE PERSONALITY AND SOUL section mirrors EN with DE-appropriate register
notes. Critical addition: domain note excludes DE career writing from
soul-adding (DE Anschreiben register is formal-modest, opposite of US/UK
puffery — adding soul makes them weaker).

blader#7 DE AI Vocabulary trigger list: 33 phrases combining mined tokens
(darüber hinaus, zusammenfassend, ganzheitlich, vorliegenden, umfassende,
darstellt) with manually curated additions (vielfältig, facettenreich,
nachhaltig, innovativ, zukunftsweisend, transformativ, ganzheitlich,
intuitiv, nahtlos, robust, im Hinblick auf, vor diesem Hintergrund,
es ist wichtig zu betonen, zentrale Rolle spielen, etc.) per OQ1.

Excluded per OQ3: 6 Wikipedia-context-only DE-only entries flagged by
DE Wiki AI-Cleanup project (productivity spikes, citation format,
non-existent categories) — not applicable to general prose. Header
documents the exclusion so future contributors don't re-add them.

Tests: 207 → 211 passes (+4 DE pack tests: existence, expected pattern
IDs, PERSONALITY section presence, no overlap with universal pack).

Maintainer flagged for future review:
  blader#11 Elegant Variation — DE synonym system richer than EN, less sharp
  blader#34 Trailing Emphasis Fragments — less common in DE, signal stronger
       when present
  blader#36 Conditional Frame Stacking — overlaps with blader#102 Konjunktiv II;
       cross-referenced
  blader#8 Copula Avoidance — 'gilt als' legitimate legal term of art, apply
       lightly in legal domain
  blader#13 Passive Voice — DE academic uses passive more heavily than EN;
       SKIP in academic AND legal domains (will be enforced in Task 7
       domains/de_overrides.md)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
duathron added a commit to duathron/humanizer-ext that referenced this pull request May 28, 2026
…reer register (Phase 2 Task 7)

245-line DE override file mirroring domains/en_overrides.md schema +
extending with 5 DE-only pattern rows (blader#100-blader#104) + DE career section
(475 words) + DACH cultural-register inversion note.

Override matrix (22 rows × 6 columns: Pattern + 5 non-casual domains):
- All EN-PARALLEL pattern overrides translated to DE
- 5 maintainer-flagged DE-specific adaptations applied:
  - blader#11 Elegant Variation: light across all domains (DE richer synonym
    system; sharper signal lost when applied strictly)
  - blader#13 Passive Voice: SKIP in academic AND legal (DE academic uses
    passive MORE than EN; was SKIP only in academic for EN)
  - blader#8 Copula Avoidance: light in legal ('gilt als' legitimate legal
    term of art)
  - blader#34 Trailing Fragments: kept strict where EN was strict (less common
    in DE, signal stronger when present)
  - blader#36 / blader#102 cross-reference (Konjunktiv overlaps in academic + legal)
- 5 DE-only pattern rows:
  - blader#100 Akademische Rahmen-Floskel: strict everywhere (even academic)
  - blader#101 Impersonales Reflexiv: light in academic + legal, strict elsewhere
  - blader#102 Konjunktiv II: light in academic + legal, strict elsewhere
  - blader#103 Anglizismen-Leakage: light in technical + marketing, strict elsewhere
  - blader#104 Nominalstil-Inflation: SKIP in legal (DE Behördendeutsch),
    light in academic, strict elsewhere

DE-specific domain guidance paragraphs:
- academic: DE passive + Nominalstil heavier than EN; blader#101 + blader#104 softened
- legal: Konjunktiv II for indirect speech is standard; blader#102 softened;
  'gilt als'/'fungiert als' can be legal terms of art
- technical: Anglizismen-Leakage softened (English tech terms unavoidable);
  flag denglisch verb constructions strictly
- marketing: Denglisch in marketing is register marker (softened); DE
  buzzword-in-phrase rule + brand-tier audit step + 5-point preserve-
  everything checklist with DE examples
- career: DACH formal-modest register (INVERSE of US/UK assertive
  self-promotion that EN career assumes). DE-specific AI tells: 'Mit
  großem Interesse', 'leidenschaftlich', 'ergebnisorientiert',
  'ganzheitlich denkend', 'es würde mich außerordentlich freuen', etc.
  All 5 career preserve rules in DE (Metriken sind heilig, Eigennamen +
  Daten + Titel, Fachvokabular, Stellenausschreibungs-Schlüsselphrasen,
  konkrete Achievement-Aussagen).
- casual: 'Ich' more weighty in DE; 'Man' constructions acceptable.
  Critical casual constraint (concept-noun preservation) translated.

Tests: 211 -> 214 (+3 DE override tests: existence, table+guidance,
pattern ID validity).

Maintainer flags for review:
- blader#11 'light' across ALL domains is broader than EN; relax to strict in
  career if FP eval over-softens
- blader#15 included for EN symmetry, not strictly required by Task 7 spec
- blader#36/blader#102 cross-reference is in a trailing blockquote (not inline);
  may want inline academic + legal mentions for visibility

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants